package com.tansun.easycare.rule.deployonline.service.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.dbunit.DatabaseUnitException;
import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.ForwardOnlyResultSetTableFactory;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.QueryDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.ext.db2.Db2DataTypeFactory;
import org.dbunit.ext.db2.Db2MetadataHandler;
import org.dbunit.ext.mysql.MySqlDataTypeFactory;
import org.dbunit.ext.oracle.OracleDataTypeFactory;
import org.dbunit.ext.postgresql.PostgresqlDataTypeFactory;
import org.drools.core.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import com.tansun.easycare.core.config.Global;
import com.tansun.easycare.rule.bakproject.service.RuleBakTableConst;
import com.tansun.easycare.rule.deployonline.entity.RuleDeployment;
import com.tansun.easycare.rule.deployonline.entity.RuleEnterpoint;
import com.tansun.easycare.rule.deployonline.service.RuleDbunitDownService;
import com.tansun.easycare.rule.deployonline.service.RuleDeployOnlineService;
import com.tansun.easycare.rule.deployonline.service.RuleEnterpointService;
import com.tansun.easycare.rule.exception.BizException;
import com.tansun.easycare.rule.ruleflow.service.RuleFlowService;
import com.tansun.easycare.rule.rulemange.entity.RuleManage;
import com.tansun.easycare.rule.rulemange.service.RuleManageService;
import com.tansun.easycare.rule.ruleproj.entity.RuleAuthority;
import com.tansun.easycare.rule.ruleproj.entity.RuleProject;
import com.tansun.easycare.rule.ruleproj.service.RuleAuthorityService;
import com.tansun.easycare.rule.ruleproj.service.RuleProjectService;
import com.tansun.easycare.rule.ruleversion.entity.RuleVersioninfo;
import com.tansun.rule.utils.ZipUtils;

@Service
public class RuleDbunitDownServiceIml implements RuleDbunitDownService {
    @Autowired
    private DataSource springDataSource;
    @Autowired
    private RuleDeployOnlineService ruleDeployOnlineService;
    @Autowired
    private RuleEnterpointService ruleEnterpointService;
    @Autowired
    private RuleFlowService ruleFlowService;
    @Autowired
    private RuleManageService ruleManageService;
    @Autowired
    private RuleProjectService ruleProjectService;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private RuleAuthorityService ruleAuthorityService;

    @Override
    public InputStream dbunitDownRuleFlow(String projectCode, String deploymentId,String[] enterpointIds) throws BizException {
        doDbunitDownRuleFlow(projectCode, deploymentId,enterpointIds);
        String ruleBakPath = Global.getConfig("RuleProject.BakPath") + File.separator + projectCode;
        File file = new File(ruleBakPath);
        FileInputStream fis = null;
        if (file.exists()) {
            ZipUtils.antzip(ruleBakPath, ruleBakPath + ".zip", true);
            File zipFile = new File(ruleBakPath + ".zip");
            try {
                fis = new FileInputStream(zipFile);
            } catch (Exception e) {
                throw new BizException(e, "");
            }

        }
        return fis;
    }

    /**
     * 根据部署id获取到涉及到的响应的表
     *
     * @param projectCode
     * @param deploymentId
     */
    private void doDbunitDownRuleFlow(String projectCode, String deploymentId,String[] enterpointIds) {
        System.out.println("the input param:"+deploymentId+",enterpoints:"+enterpointIds);
        try {
            IDatabaseConnection conn = createDataConnection();
            DatabaseConfig dataConfig = conn.getConfig();
            setDataConfig(dataConfig);
            String encode = Global.getConfig("RuleProject.Back.Encoding");
            QueryDataSet dataSet = null;
            //根据部署id获取到该规则流所涉及到的组件，并查询出涉及到的表,其中key为涉及到的表，list为该表下的id   ljt
            String ruleBakPath = Global.getConfig("RuleProject.BakPath") + File.separator + projectCode;
            File file = new File(ruleBakPath);
            if (file.exists()) {
                file.delete();
            }
            file.mkdirs();
            //key值为表名称，集合为对应的id集
            Map<String, List<String>> tmaps = getTables(projectCode, deploymentId,enterpointIds);
            System.out.println("************tmaps*************************:"+tmaps);
            //将引用到的数据集以及变量的数据包括rule_flow_rects中的脚本和规则函数信息查询出
            creatDsAndDpAndFunc(projectCode,tmaps);
            String sql = "";
            List<String> tables =RuleBakTableConst.readRuleTable();
            for(String tablename:tables){
                dataSet = new QueryDataSet(conn);
                if (StringUtils.isEmpty(encode)) {
                    encode = "UTF-8";
                }
                if(!tmaps.containsKey(tablename)){
                    //导出空数据
                    sql =  "select DISTINCT  null from "+tablename;
                    dataSet.addTable(tablename, sql);
                    FlatXmlDataSet.write(dataSet, new FileWriter(ruleBakPath + File.separator + tablename + ".xml"), encode);
                    continue;
                }
                List<String> value = tmaps.get(tablename);
                String ids = listToString(value);
                if (tablename.toLowerCase().equals("rule_versioninfo") || tablename.toLowerCase().equals("rule_flow")
                        || tablename.toLowerCase().equals("rule_group") || tablename.toLowerCase().equals("rule_codeblock")) {
                    dataConfig.setProperty(DatabaseConfig.PROPERTY_RESULTSET_TABLE_FACTORY, new ForwardOnlyResultSetTableFactory());
                }
                if (tablename.toLowerCase().equals("rule_project") || tablename.toLowerCase().equals("rule_authority")) {
                    sql = "select  *   from " + tablename + "  where projectcode= '" + projectCode + "'";
                } else if (tablename.toLowerCase().equals("rule_data_domain")) {
                    if(value!=null&&value.size()>0){
                        sql = "select  a.*   from " + tablename + " a , RULE_DATAPARAMS  b  where  a.DOMAIN_ID = b.ID and  b.projectcode='" + projectCode + "' and b.ID IN "+ids;
                    }else{
                        sql =  "select DISTINCT  null from "+tablename;
                    }
                }else if (tablename.toLowerCase().equals("rule_manage")) {//RULE_MANAGE
                    sql = "select  a.*   from RULE_MANAGE a  where  a.project_code = '"+projectCode+"' and a.id in "+ids;
                }else if(tablename.toLowerCase().equals("rule_reference_data")){
                    sql = "select  a.*   from " + tablename + " a where  a.project_code = '"+projectCode+"' and a.id in "+ids;
                }else if(tablename.toLowerCase().equals("rule_dataset")||tablename.toLowerCase().equals("rule_dataparams")){
                    if(value!=null&&value.size()>0){
                        sql = "select  a.*   from " + tablename + " a where  a.projectcode = '"+projectCode+"' and a.id in "+ids;
                    }else{
                        sql =  "select DISTINCT  null from "+tablename;
                    }
                }else if(tablename.toLowerCase().equals("rule_enum")||tablename.toLowerCase().equals("rule_excution_block")){
                    sql =  "select DISTINCT  null from "+tablename;
                }else if(tablename.toLowerCase().equals("rule_serial_no")){
                    sql = "select * from rule_serial_no where project_code = '"+projectCode+"'";
                } else if(tablename.toLowerCase().equals("rule_codeblock")){
                    if(value!=null&&value.size()>0){
                        sql = "select * from rule_codeblock where project_code = '"+projectCode+"' and manage_id in "+ids;
                    }else{
                        sql = "select DISTINCT  null from "+tablename;
                    }
                } else{
                    //其他组件报数数据的表
                    sql = "select  a.*   from " + tablename + " a where  a.project_code = '"+projectCode+"' and a.id in "+ids;
                }
                dataSet.addTable(tablename, sql);
                FlatXmlDataSet.write(dataSet, new FileWriter(ruleBakPath + File.separator + tablename + ".xml"), encode);
            }
        } catch (Exception e) {
            throw new BizException(e, "E-005001");

        }

    }

    /**
     * 获取该规则流中所涉及到的表
     *
     * @param projectCode
     * @param deploymentId
     * @return
     */
    private Map<String,List<String>> getTables(String projectCode, String deploymentId,String[] enterpointIds) {
        Map<String,List<String>> tmaps = new HashMap<>();
        RuleDeployment deployment = this.ruleDeployOnlineService.get(deploymentId);
        //加入项目最原始的信息（RULE_SERIAL_NO信息 RULE_EXCUTION_BLOCK   RULE_ENUM）
        tmaps.put("RULE_DEPLOYMENT",new ArrayList<String>(){{add(deploymentId);}});
        tmaps.put("RULE_SERIAL_NO",new ArrayList<>());
        tmaps.put("RULE_EXCUTION_BLOCK",new ArrayList<>());
        tmaps.put("RULE_ENUM",new ArrayList<>());
        tmaps.put("RULE_CODEBLOCK",new ArrayList<>());
        //加入RULE_PROJECT的数据
        RuleProject ruleProject = new RuleProject();
        ruleProject.setProjectcode(projectCode);
        List<RuleProject> list = ruleProjectService.findList(ruleProject);
        tmaps.put("RULE_PROJECT",new ArrayList<String >(){{add(list.get(0).getId());}});
        //加入RULE_AUTHORITY数据
        RuleAuthority ruleAuthority = new RuleAuthority();
        ruleAuthority.setProjectcode(projectCode);
        List<RuleAuthority> ruleAuthoritys = ruleAuthorityService.findList(ruleAuthority);
        tmaps.put("RULE_AUTHORITY",new ArrayList<String >(){{add(ruleAuthoritys.get(0).getId());}});
        //需优化
//        List<RuleEnterpoint> searchEnterPoints = new ArrayList<>();
//        for(String id :enterpointIds){
//            RuleEnterpoint enterInfo = new RuleEnterpoint();
//            enterInfo.setDeploymentId(deployment.getId());
//            enterInfo.setId(id);
//            List<RuleEnterpoint> enterPoints = this.ruleEnterpointService.findList(enterInfo);
//            searchEnterPoints.add(enterPoints.get(0));
//        }
        List<String> enpointIdList = Arrays.asList(enterpointIds);
        List<RuleEnterpoint> searchEnterPoints = ruleEnterpointService.findByDeployId(deploymentId, enpointIdList);
        for (RuleEnterpoint ruleEnterpoint : searchEnterPoints) {
            String[] ruleflowinfo = ruleEnterpoint.getRuleflowId().split("_");
            List<RuleVersioninfo> tmp = this.ruleFlowService.findRuleFlowPublishVersion(projectCode, ruleflowinfo[0],null);
            //加入enterpoint信息
            if(tmaps.containsKey("RULE_ENTERPOINT")){
                tmaps.get("RULE_ENTERPOINT").add(ruleEnterpoint.getId());
            }else{
                tmaps.put("RULE_ENTERPOINT",new ArrayList<String>(){{add(ruleEnterpoint.getId());}});
            }
            tables(tmp,tmaps);
        }
        return  tmaps;
    }

    private void tables(List<RuleVersioninfo> tmp,Map<String,List<String>> tmaps) {
        RuleVersioninfo remove = tmp.remove(tmp.size() - 1);//规则流本身的信息，以便区分子流程
        String manageId = remove.getManageId();
        RuleManage ruleManage = ruleManageService.get(manageId);
        //加入规则流本身的数据信息
        if(tmaps.get("RULE_MANAGE")!=null&&tmaps.get("RULE_MANAGE").size()>0){
            tmaps.get("RULE_MANAGE").add(ruleManage.getId());
        }else{
            tmaps.put("RULE_MANAGE",new ArrayList<String >(){{add(ruleManage.getId());}});
        }
        //规则流所对应的信息也要导出
        createTablesMap(ruleManage,Arrays.asList("RULE_FLOW","RULE_FLOW_CONDITIONS","RULE_FLOW_RECTS","RULE_VERSIONINFO"),tmaps);
        createModelAuthorty(ruleManage.getProjectCode(),ruleManage.getId(),tmaps);
        for (RuleVersioninfo ruleVersioninfo : tmp) {
            //加入流程下所有组件的数据
            RuleManage refManage = ruleManageService.get(ruleVersioninfo.getManageId());
            getTableByManage(refManage,tmaps);
            //加入组件自身的RULE_MANAGE信息
            if(tmaps.get("RULE_MANAGE")!=null&&tmaps.get("RULE_MANAGE").size()>0){
                tmaps.get("RULE_MANAGE").add(refManage.getId());
            }else{
                tmaps.put("RULE_MANAGE",new ArrayList<String >(){{add(refManage.getId());}});
            }
            createModelAuthorty(refManage.getProjectCode(),refManage.getId(),tmaps);
        }
    }

    //根据组件型获取到组件对应的表
    private void getTableByManage(RuleManage ruleManage,Map<String,List<String>> tmaps) {
        //1.决策树 3.决策表 5.一维表索引 6.评分模型  23规则集      7.规则流  19。规则函数        9.脚本  21规则
        String modelType = ruleManage.getModelType();
        List<String> keys = new ArrayList<>();
        if ("01".equals(modelType)) {
               //RULE_TREE_CONDITIONSs RULE_TREE_NODE RULE_TREE_PARAM rule_scene
            keys = Arrays.asList("RULE_TREE_CONDITIONS","RULE_TREE_NODE","RULE_TREE_PARAM","RULE_TREE_SCENE","RULE_REFERENCE_DATA","RULE_VERSIONINFO");
        } else if ("03".equals(modelType)|| "05".equals(modelType)) {
            keys = Arrays.asList("RULE_DECISIONTABLE_DETAIL","RULE_REFERENCE_DATA","RULE_VERSIONINFO");
        }  else if ("06".equals(modelType)) {
            keys = Arrays.asList("RULE_REFERENCE_DATA","RULE_VERSIONINFO","RULE_SCORE","RULE_SCORE_INFO");
        } else if ("07".equals(modelType)) {
            String code = ruleManage.getCode();
            String version = ruleManage.getVersion();
            List<RuleVersioninfo> tmp = this.ruleFlowService.findRuleFlowPublishVersion(ruleManage.getProjectCode(), code,version);
            tables(tmp,tmaps);//递归执行
        } else if ("19".equals(modelType)) {
            keys = Arrays.asList("RULE_VERSIONINFO");
        } else if ("23".equals(modelType)) {
            keys = Arrays.asList("RULE_VERSIONINFO","RULE_REFERENCE_DATA","RULE_GROUP");
        }else if("09".equals(modelType)){
            keys = Arrays.asList("RULE_CODEBLOCK");
        }else if("21".equals(modelType)){
            keys = Arrays.asList("RULE_GROUP");
        }
        createTablesMap(ruleManage,keys,tmaps);
    }

    private void createTablesMap(RuleManage ruleManage,List<String> keys,Map<String, List<String>> tMap){
       for(String key : keys){
           List<Map<String, Object>> maps = new ArrayList<>();
           String sql = "select id from "+key+" where manage_id = ? and version = ?";
           if("19".equals(ruleManage.getModelType())){
               maps = jdbcTemplate.queryForList(sql, ruleManage.getProjectCode(),ruleManage.getVersion());
           }else{
               maps = jdbcTemplate.queryForList(sql,ruleManage.getId(),ruleManage.getVersion());
           }
           for(Map<String,Object> map : maps){
               String referenceId = map.get("id").toString();
               List<String> ids= tMap.get(key);
               if(ids!=null&&ids.size()>0){
                   ids.add(referenceId);
               }else{
                   tMap.put(key,new ArrayList<String>(){{add(referenceId);}});
               }
           }
       }
    }

    //获取数据集和变量的id
    private  List<String> getIds(String projectcode,String ids,String type){
        List<String> idList = new ArrayList<>();
        String sql = "";
        if("1".equals(type)){
             sql = "SELECT rd.id FROM RULE_DATASET rd WHERE rd.projectcode = '"+projectcode+"' AND rd.name IN (\n" +
                    "SELECT  DISTINCT rr.class_name FROM RULE_REFERENCE_DATA rr WHERE rr.project_code = '"+projectcode+"' AND rr.id \n" +
                    "IN "+ids+")";
        }else if("2".equals(type)){
            sql = "SELECT id FROM RULE_DATAPARAMS WHERE projectcode = '"+projectcode+"' AND NAME IN (\n" +
                    "SELECT  DISTINCT nameen FROM RULE_REFERENCE_DATA WHERE project_code = '"+projectcode+"' AND id \n" +
                    "IN "+ids+")";
        }else if("3".equals(type)){
            sql = "SELECT rect_code FROM RULE_FLOW_RECTS WHERE project_code = '"+projectcode+"' AND RECT_TYPE = 'codeblock' AND id IN "+ids;
        }
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        for(Map<String,Object> map : maps){
            if("3".equals(type)){
                String rectCode = map.get("rect_code").toString();
                RuleManage ruleManage = new RuleManage();
                ruleManage.setCode(rectCode);
                ruleManage.setProjectCode(projectcode);
                List<RuleManage> list = ruleManageService.findList(ruleManage);
                idList.add(list.get(0).getId());
            }else{
                idList.add(map.get("id").toString());
            }
        }
        return idList;
    }

    /**
     * 通过RULE_REFERENCE_DATA查询出数据集和变量
     */
    private  void creatDsAndDpAndFunc(String projectCode,Map<String, List<String>> tMap){
        List<String> refIds = tMap.get("RULE_REFERENCE_DATA");
        if(refIds!=null&&refIds.size()>0){
            List<String> dsList = getIds(projectCode, listToString(refIds), "1");
            List<String> dpList = getIds(projectCode, listToString(refIds), "2");
            tMap.put("RULE_DATASET",dsList);
            tMap.put("RULE_DATAPARAMS",dpList);
            tMap.put("RULE_DATA_DOMAIN",dpList);
        }
        List<String> rectIds = tMap.get("RULE_FLOW_RECTS");
        if(rectIds!=null&&rectIds.size()>0){
            List<String> codeBlockIds = getIds(projectCode, listToString(rectIds), "3");
            tMap.put("RULE_CODEBLOCK",codeBlockIds);
            tMap.get("RULE_MANAGE").addAll(codeBlockIds);
            for(String manageId : codeBlockIds){
                createModelAuthorty(projectCode,manageId,tMap);
            }
        }
    }

    private void createModelAuthorty(String projectCode,String manageId,Map<String, List<String>> tMap){
        String sql = "select id from RULE_MODEL_AUTHORITY where project_code = ? and manage_id = ?";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, projectCode, manageId);
        String id = maps.get(0).get("id").toString();
        List<String> ruleModelAuthorityList = tMap.get("RULE_MODEL_AUTHORITY");
        if(ruleModelAuthorityList!=null&&ruleModelAuthorityList.size()>0){
            tMap.get("RULE_MODEL_AUTHORITY").add(id);
        }else{
            tMap.put("RULE_MODEL_AUTHORITY",new ArrayList<String>(){{add(id);}});
        }
    }

    private IDatabaseConnection createDataConnection() throws DatabaseUnitException, SQLException {
        IDatabaseConnection conn = null;
        String type = Global.getConfig("jdbc.type");
        if (type.equals("oracle")) {
            String username = Global.getConfig("jdbc.username");
            conn = new DatabaseConnection(springDataSource.getConnection(), username);
        } else if (type.equals("postgre")) {
            String schema = Global.getConfig("RuleProject.res.schema");
            conn = new DatabaseConnection(springDataSource.getConnection(), schema);
        } else if (type.equals("db2")) {
            String schema = Global.getConfig("RuleProject.res.schema");
            conn = new DatabaseConnection(springDataSource.getConnection(), schema);
        } else if (type.equals("mysql")) {
            conn = new DatabaseConnection(springDataSource.getConnection());
        } else {
            conn = new DatabaseConnection(springDataSource.getConnection());
        }
        return conn;
    }

    private void setDataConfig(DatabaseConfig dataConfig) {
        String type = Global.getConfig("jdbc.type");
        if (type.equals("mysql")) {
            dataConfig.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new MySqlDataTypeFactory());
        } else if (type.equals("oracle")) {
            dataConfig.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new OracleDataTypeFactory());
        } else if (type.equals("postgre")) {
            dataConfig.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new PostgresqlDataTypeFactory());
        } else if (type.equals("db2")) {
            dataConfig.setProperty(DatabaseConfig.PROPERTY_METADATA_HANDLER, new Db2MetadataHandler());
            dataConfig.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new Db2DataTypeFactory());
        }
    }
    public String listToString(List<String> list){
        StringBuffer buffer = new StringBuffer();
        if(list!=null){
            for(int i=0;i<list.size();i++){
                if(i==0){
                    buffer.append("('")
                            .append(list.get(i)).append("'");
                    if(1==list.size()){
                        buffer.append(")");
                        break;
                    }
                    continue;
                }
                buffer.append(",'").append(list.get(i)).append("'");
                if(i==list.size()-1){
                    buffer.append(")");
                    break;
                }

            }
        }

        return buffer.toString();
    }
    private String arrayToString(String[] list){
        StringBuffer buffer = new StringBuffer();
        if(list!=null){
            for(int i=0;i<list.length;i++){
                if(i==0){
                    buffer.append("('")
                            .append(list[i]).append("'");
                    if(1==list.length){
                        buffer.append(")");
                        break;
                    }
                    continue;
                }
                buffer.append(",'").append(list[i]).append("'");
                if(i==list.length-1){
                    buffer.append(")");
                    break;
                }

            }
        }

        return buffer.toString();
    }
}
